package jp.test;



import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.Signature;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Random;

/*
import javax.media.Format;
import javax.media.MediaLocator;
import javax.media.rtp.SessionListener;
*/

import jp.lab1.SecureNode;
import jp.lab1.SecureNodeList;
import jp.lab2.AVTransmit3;

import org.cybergarage.http.HTTP;
import org.cybergarage.http.HTTPRequest;

import org.cybergarage.upnp.Action;
import org.cybergarage.upnp.Argument;
import org.cybergarage.upnp.ArgumentList;
import org.cybergarage.upnp.Device;
import org.cybergarage.upnp.Service;
import org.cybergarage.upnp.ServiceList;
import org.cybergarage.upnp.StateVariable;
import org.cybergarage.upnp.UPnP;
import org.cybergarage.upnp.UPnPStatus;
import org.cybergarage.upnp.control.ActionListener;
import org.cybergarage.upnp.control.ActionRequest;
import org.cybergarage.upnp.control.ActionResponse;
import org.cybergarage.upnp.control.QueryListener;
import org.cybergarage.upnp.device.InvalidDescriptionException;

import org.cybergarage.util.Debug;

import org.geed.util.UPnPCipher;

import com.vladium.utils.SystemInformation;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class DDevice extends Device implements ActionListener
					,QueryListener{
	

	/*
	 * ***********DEVICE SECURITY
	 */
	private int keylen = 16;
	private int l = 128; //ramda
	
	private byte[][] secretBytes;
	private byte[][] publicIDBytes;
	//private String secretStr ;
	//private String publicIDStr;
	
	private byte[] shareSecret; //dynamic
	private byte[] tmpSessionKey = null; //PKI
	private String tmpSessionKeyEncoded; //PKI
	//private String shareSecretID;
	private byte[] groupKey = new byte[keylen];
	private String groupKeyEncoded;
	StateVariable groupKeyIDVar;
	private String shareKeyID = "001";
	
	private boolean decryptandexecuting = false;
	private boolean nodeRegistration = false;

	String remotePubKey = null; //temporary authenticate function
	String tmpLTSB = "";
	
	private String broadcastVideoURI = "224.1.2.4/42050";
	private String broadcastAudioURI = "224.1.2.4/42052";
	
	private String avFilePath = "file:/C:/video.avi";
	private String broadcastAddress = "224.1.2.4";
	private String broadcastPort = "42050";
	
	
	
	/*
	 * ##########DEVICE SECURITY SUPPORT #####
	 * 
	 */
	
	//GetLifeTimeSequenceBased
	private String getLifeTimeSequenceBased(){
		Date now = new Date();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
		String rt = sdf.format(now);
		
		return rt;
	}
	
	private void startDecryptAndExecuting(){
		this.decryptandexecuting = true;
	}
	
	private void stopDecryptAndExecuting(){
		this.decryptandexecuting = false;
	}
	
	private boolean isDecryptAndExecuting(){
		return decryptandexecuting;
	}
	
	private void setNodeRegistration(boolean regist){
		this.nodeRegistration = regist;
	}
	
	private boolean getNodeRegistration(){
		return this.nodeRegistration;
	}
	
	private boolean isExecutable(){
		return isDecryptAndExecuting() && getNodeRegistration();
	}
	
	
	private void readKeyFile(){
		try{
			// Read key bytes (8)
			FileInputStream in = new FileInputStream("secret\\m0.txt");
			
		
			this.secretBytes = new byte[l+1][keylen];
			this.publicIDBytes = new byte[l+1][keylen];
			

			
			for(int i=0; i<l+1; i++){
				in.read(this.secretBytes[i], 0, this.secretBytes[i].length);				
			}
			
			
			for(int i=0; i<l+1; i++){
				in.read(this.publicIDBytes[i], 0, this.publicIDBytes[i].length);
				//System.out.print(this.publicIDBytes[i]+" ");
			}
			
		}catch(IOException ioe){
			ioe.printStackTrace();
		}
		
	}
	
	private String readPublicStr(){
		//make 2 dimension arrays into one long array
    	byte[] tmpPubID = new byte[(l+1)*keylen];
    	int current = 0;
    	for(int i=0; i<l+1; i++){
    		System.arraycopy(publicIDBytes[i], 0, tmpPubID, current, keylen);
    		current += keylen;
    	}
    	BASE64Encoder base64Enc = new BASE64Encoder();
    	return base64Enc.encode(tmpPubID);
	}
	
    private byte[] computeSecret(byte[] publicIDBytes){
		// see full verion at org.geed.kpd.test.TestKey
		
		byte[][] publicID = new byte[l+1][keylen];
		int current =0;
		for(int i=0; i<l+1; i++){
			System.arraycopy(publicIDBytes, current, publicID[i], 0, keylen);
			current += keylen;
		}
		
		
		
		byte[] result = new byte[keylen];
		for(int i=0; i<l+1; i++){
			byte[] tmp = new byte[keylen];
			
			for(int j=0; j<keylen; j++){
				tmp[j] = (byte)(this.secretBytes[i][j]*publicID[i][j]);
			}
			
			for(int j=0; j<keylen; j++){
				result[j] += tmp[j];
			}
			
			
		}
		
		//set secret bytes use to generate secret key
		return result;
	}
    private void leaveRekey(){
    	//update groupkey
		Random rand = new Random();
		byte[] b = new byte[keylen];

		rand.nextBytes(b);
		for(int i=0; i<keylen; i++){
			this.groupKey[i] = b[i];
		}
		
		
		//change groupkey id
		String currentGroupKeyIDStr = this.groupKeyIDVar.getValue();
		int currentGroupKeyIDInt = Integer.parseInt(currentGroupKeyIDStr);
		currentGroupKeyIDInt++;
		
		long startTime = System.currentTimeMillis();
		this.groupKeyIDVar.setValue(currentGroupKeyIDInt);
		long finisgTime = System.currentTimeMillis();
		
		long espTime = finisgTime - startTime;
		int nodeSize = this.groupKeyIDVar.getService().getSubscriberList().size();
		
		String logStr = "node_size "+nodeSize+" rekey_elapsed_time "+espTime+HTTP.CRLF;
		try{
			FileOutputStream f_out = new FileOutputStream("time\\leave_rekey_time.log", true);
			f_out.write(logStr.getBytes());
			f_out.close();
		}catch(Exception e){}
		
		
		System.err.println(logStr);
		
    }
    
    private void joinRekey(){ //multicast
    	Random rand = new Random();
    	byte[] b = new byte[keylen];
    	
    	rand.nextBytes(b);
    	
    	//byte[] groupKeyOld = this.groupKey;

    	//this.groupKey = b; //rekey
    	
    	for(int i=0; i<keylen; i++){
			this.groupKey[i] = b[i];
		}
    	
    	
    	//change groupkey id
		String currentGroupKeyIDStr = this.groupKeyIDVar.getValue();
		int currentGroupKeyIDInt = Integer.parseInt(currentGroupKeyIDStr);
		currentGroupKeyIDInt++;
		
		//set statevariable without notify
		//this.groupKeyIDVar.getStateVariableData().setValue(""+currentGroupKeyIDInt);
		this.groupKeyIDVar.setValue(currentGroupKeyIDInt);
    	
    	
    	/*
    	if(groupKeyOld == null){// 1st join
    		return;
    	}
    	
    	
    	//base64 encoded new groupkey
		BASE64Encoder base64enc = new BASE64Encoder();
		String groupKeyStr = base64enc.encode(this.groupKey);

		//encode with old groupkey
		UPnPCipher.setKey(groupKeyOld);
		UPnPCipher.init();
		String groupKeyNewEncoded = UPnPCipher.encode(groupKeyStr);
		
		//notify to all member modify from Service.notify()
		String varName = "GroupKeyEncoded";
		String value = groupKeyNewEncoded;
		
		String host = SSDP.ADDRESS;
		int port = 50929;
		String bindAddr = HostInterface.getHostAddress(0);
		
		NotifyRequest notifyReq = new NotifyRequest();
		notifyReq.setMethod(HTTP.NOTIFY);
		notifyReq.setURI("");
		notifyReq.setHost(bindAddr, port);
		notifyReq.setNT(NT.EVENT);
		notifyReq.setNTS(NTS.PROPCHANGE);
		notifyReq.setSID("uuid:2222-2222-2222-2222");
		notifyReq.setSEQ(0);
		
		notifyReq.setContentType(XML.CONTENT_TYPE);
		//create xml node
		Node propSetNode = new Node("propertyset");
		propSetNode.setNameSpace("e", Subscription.XMLNS);
		
		Node propNode = new Node("property");
		propSetNode.addNode(propNode);
		
		Node varNameNode = new Node(varName);
		varNameNode.setValue(value);
		propNode.addNode(varNameNode);
		
		System.out.println("content str ===>"+propSetNode.toString());
		notifyReq.setContent(propSetNode);
		
		//multicast notify
		StringBuffer str = new StringBuffer();
		str.append(notifyReq.getHeader());
		str.append(HTTP.CRLF);
		str.append(propSetNode.toString());
		str.append(HTTP.CRLF);
		String msg = new String(str);
		
		System.out.println("Notify multicast ====>"+msg);
		
		try{
			MulticastSocket msock = new MulticastSocket();
			InetSocketAddress multigroup = new InetSocketAddress(InetAddress.getByName(host),port);
			
			DatagramPacket dgmPacket = new DatagramPacket(msg.getBytes(),msg.length(), multigroup);
			msock.setTimeToLive(4);
			msock.send(dgmPacket);
			msock.close();
			
		}catch(Exception e){
			e.printStackTrace();
		}
		
		
		*/
		
    	
    }
                         
    
 	
	/*
	 * ###### DEVICE SECURITY EXTENDED ################
	 */
    synchronized private boolean authen(String sequenceBase, String authSecret){
		//this.shareSecretID = sid;
    	//System.out.println("Authenticate KPS based");
		if(sequenceBase.equals(tmpLTSB)== false){
			return false;
		}
		String salt = "1234567890123456789012345678901234567890123456789012345678901234567890123456789012312345";
		String salt2 = "0123456789012345678901234567890123456789012345678901234567890123456789";

		// compute share secret
		BASE64Decoder base64Dec = new BASE64Decoder();
		byte[] remotePubKeyByte = null;
		try{
			remotePubKeyByte = base64Dec.decodeBuffer(this.remotePubKey);
			
		}catch(IOException ioe){
			ioe.printStackTrace();
		}
		
		
		//get value			
		//final SystemInformation.CPUUsageSnapshot m_prevSnapshot =
		//	SystemInformation.makeCPUUsageSnapshot ();
		long startTime = System.nanoTime();

		byte[] secret = computeSecret(remotePubKeyByte);
		
		long finishTime = System.nanoTime();
		
		long espTime = finishTime - startTime;
		System.out.print("computeSecret_time "+espTime);
		
		this.shareSecret = secret; //1 computesecret
		
		//init cipher and decode authsecret

		//System.out.println("SequenceBase = "+this.tmpLTSB);
		
		
		//String authStr = UPnPCipher.decode(secret, authSecret);
		//change to H(LTSB+Secret)
		String secretStr = null;
		try{
			secretStr = new String(secret,"UTF-8");
		}catch(Exception e){}
		
		//System.out.println("SecretKey Bytes = "+secretStr);
		
		String toHashed = this.tmpLTSB+secretStr+salt+salt2;
		String myAuthSecret = null;
		
		startTime = System.nanoTime();
		//digest message
		try{
			MessageDigest algorithm = MessageDigest.getInstance("SHA1");
			algorithm.reset();
			algorithm.update(toHashed.getBytes("UTF-8"));
			byte[] b = algorithm.digest(); //2 digest
			myAuthSecret = new BASE64Encoder().encode(b);
			
		}catch(Exception e){
			e.printStackTrace();
		}
		finishTime = System.nanoTime();
		espTime = finishTime - startTime;
		System.out.print(" digest_time "+espTime);
		
		boolean authenResult = authSecret.equals(myAuthSecret);
		
		//cpu usage measure
		//final SystemInformation.CPUUsageSnapshot event = 
		//	SystemInformation.makeCPUUsageSnapshot ();
		
		//double receivedCPUUsage = 100.0 * SystemInformation.getProcessCPUUsage (m_prevSnapshot, event);
		//System.out.println(receivedCPUUsage+" %");
		//debug
		//System.out.println("Authen STR="+authStr);
		//System.out.println("Remote Authsecret = "+authSecret);
		//System.out.println("My     Authsecret = "+myAuthSecret);
		
		
		if(authenResult == true){
			//leaveRekey();
			joinRekey();
			
			//update groupkey encoded
			BASE64Encoder base64enc = new BASE64Encoder();
			String groupKeyStr = base64enc.encode(this.groupKey);

			startTime = System.nanoTime();
			this.groupKeyEncoded = UPnPCipher.encode(this.shareSecret, groupKeyStr);
			finishTime = System.nanoTime();
			espTime = finishTime - startTime;
			System.out.print(" Encrypt_time "+espTime);

			return true;
		}
		System.out.println("Authentication failed" );
		return false;
	}
    
    synchronized private boolean authenPKI(String sequenceBase, String authSecret){
    	
    	if(sequenceBase.equals(tmpLTSB) == false)
    		return false;
    	
    	String salt = "12345678901234567890123456789012345678901234567890123456789012345678901234567890123123459876543219876541";
    	String salt2 = "0123456789012345678901234567890123456789012345678901234567890123456789";
    	
    	String sigStr = this.tmpLTSB + salt + salt2;
    	
    	//load cp's certificate to virify ltsb
    	//verify LTSB
    	try{
    		//verify certificate
    		
			ByteArrayInputStream bin = new ByteArrayInputStream(this.tmpCertInBytes);
			
			//generate certificate
			CertificateFactory cf = CertificateFactory.getInstance("X509");
			Certificate certIn = cf.generateCertificate(bin);
			
			//verify certin
			loadCACertificate(caCertificatePath);
			
			long startTime = System.nanoTime();
			certIn.verify(caCert.getPublicKey());
			long finishTime = System.nanoTime();
    		long espTime = finishTime - startTime;
    		System.out.print("Verify_Cert "+espTime);
			
			this.tempCertIn = certIn;
			
    		//verify signature
    		Signature sign = Signature.getInstance("SHA1WithRSA");
    		sign.initVerify(certIn);
    		sign.update(sigStr.getBytes());
    		
    		byte[] authSecretBytes = new BASE64Decoder().decodeBuffer(authSecret);
    		startTime = System.nanoTime();
    		boolean rt = sign.verify(authSecretBytes);
    		finishTime = System.nanoTime();
    		espTime = finishTime - startTime;
    		System.out.print(" Verify_LTSB "+espTime);
    		if(rt == false)
    			return false;
    		
    		
    		
    	}catch(Exception e){
    		e.printStackTrace();
    	}
		

		//random session key
		Random rand = new Random();
    	byte[] b = new byte[keylen];
    	
    	rand.nextBytes(b);
    	
    	this.tmpSessionKey = b;
	
		//update group key
    	joinRekey();
		
    			
		//base64encode groupkey and session key
		BASE64Encoder base64enc = new BASE64Encoder();
		String groupKeyStr = base64enc.encode(this.groupKey);
		String sessionKeyStr = base64enc.encode(this.tmpSessionKey);
			
		//encrypt via control point pub key
		try{
			long startTime = System.nanoTime();
			this.groupKeyEncoded = UPnPCipher.encodeRSA(this.tempCertIn.getEncoded(), groupKeyStr);
			this.tmpSessionKeyEncoded = UPnPCipher.encodeRSA(this.tempCertIn.getEncoded(), sessionKeyStr);
			long finishTime = System.nanoTime();
			long espTime = finishTime - startTime;
			System.out.print(" RSAEncrypt_time "+espTime);
		}catch(Exception e){
			e.printStackTrace();
		}
			
			
			
		return true;
		

    }
	
    private String decryptSOAP(byte[] secret, String request)
	{
		

		String oriRequest = UPnPCipher.decode(secret, request);
		
		
		//System.out.println("DecryptAndExecute ==>"+oriRequest);
		return oriRequest;
	}
	
	private String executeSOAP(byte[] secret, HTTPRequest httpRequest){
		String response = "";
		if(httpRequest.isPostRequest())
		{	
			//System.out.println("IsRequest ====>true");
			if(httpRequest.isSOAPAction()){
				//System.out.println("IsSOAPAction ====>true");
				String uri = httpRequest.getURI();
				Service ctlService = getServiceByControlURL(uri);
				if(ctlService != null){
					ActionRequest crlReq = new ActionRequest(httpRequest);
					String secureActionName = crlReq.getActionName();
					Action secureAction = ctlService.getAction(secureActionName);
					
					ArgumentList actionArgList = secureAction.getArgumentList();
					ArgumentList reqArgList = crlReq.getArgumentList();
					actionArgList.set(reqArgList);
					
					ActionResponse actionRes = new ActionResponse();
					secureAction.setStatus(UPnPStatus.INVALID_ACTION);
					actionControlReceived(secureAction);
					
					actionRes.setResponse(secureAction);
					
					response = actionRes.getHeader();
					response += HTTP.CRLF;
					response += actionRes.getContentString();
					
					
				}
			}
		}
		//System.out.println("Response ====>"+response);
		
		//encrypt response

		String encResponse = UPnPCipher.encode(secret, response);
		
		
		return encResponse;
	}
	

	
	
	/*
	 * ############# UPNP STANDARD FUNCTION ##############
	 */
	private final static String DESCRIPTION_FILE_NAME = "description/description_adevice.xml";
	
	public DDevice() throws InvalidDescriptionException{
		this(DESCRIPTION_FILE_NAME);
	}
	
	public DDevice(String descriptionFileName) throws InvalidDescriptionException
	{
		super (new File(descriptionFileName));
		
		setGC("yes");
		setSG("uuid:jakapan1234");
		
		super.setLeaseTime(180*60);
		
		Action setTimeAction = getAction("SetTime");
		setTimeAction.setActionListener(this);
		
		Action setTVPowerOffAct = getAction("TVPowerOff");
		setTVPowerOffAct.setActionListener(this);
		
		Action setTVPowerOnAct = getAction("TVPowerOn");
		setTVPowerOnAct.setActionListener(this);
		
		Action getTimeAction = getAction("GetTime");
		getTimeAction.setActionListener(this);
		
		//devicesecurity actions
		Action getGroupKey = getAction("GetGroupKey");
		getGroupKey.setActionListener(this);
		
		Action getGroupKeyID = getAction("GetGroupKeyID");
		getGroupKeyID.setActionListener(this);
		
		Action getKeyInformation = getAction("GetKeyInformation");
		getKeyInformation.setActionListener(this);
		
		//PKI support
		Action getCertificateAct = getAction("GetCertificate");
		getCertificateAct.setActionListener(this);
		
		Action authPKIAct = getAction("AuthenticatePKI");
		authPKIAct.setActionListener(this);
		
		Action getAlgorithmAndProtocols = getAction("GetAlgorithmAndProtocols");
		getAlgorithmAndProtocols.setActionListener(this);
		
		Action getLifetimeSequenceBase = getAction("GetLifetimeSequenceBase");
		getLifetimeSequenceBase.setActionListener(this);
		
		Action leaveGroup = getAction("LeaveGroup");
		leaveGroup.setActionListener(this);
		
		Action decryptAndExecute = getAction("DecryptAndExecute");
		decryptAndExecute.setActionListener(this);
		
		Action authenticate = getAction("Authenticate");
		authenticate.setActionListener(this);
		//end devicesecurity actions
		
		Action getBroadcastVideoURI = getAction("GetBroadcastVideoURI");
		getBroadcastVideoURI.setActionListener(this);
		
		Action getBroadcastAudioURI = getAction("GetBroadcastAudioURI");
		getBroadcastAudioURI.setActionListener(this);
		
		Action getGroupIDAction = getAction("GetGroupID");
		getGroupIDAction.setActionListener(this);
		
		Action getGroupSizeAction = getAction("GetGroupSize");
		getGroupSizeAction.setActionListener(this);
		
		Action getCPUSpeedAction = getAction("GetCPUSpeed");
		getCPUSpeedAction.setActionListener(this);
		
		Action getFreeMemoryAction = getAction("GetFreeMemory");
		getFreeMemoryAction.setActionListener(this);
		
		Action getGroupControllerScoreAction = getAction("GetGroupControllerScore");
		getGroupControllerScoreAction.setActionListener(this);
		
		ServiceList serviceList = getServiceList();
		Service service = serviceList.getService(0);
		service.setQueryListener(this);
		
		groupKeyIDVar = getStateVariable("GroupKeyID");
		groupKeyIDVar.setValue(100);
		
		//Debug.on();
		//String keyDStr = "thisabaathisabaa";
		//String keyDStr = "thisaboothisaboo";
		
		//read key from file
		readKeyFile();
		
		
		//UPnPCipher.setKey(keyDStr.getBytes());
		//UPnPCipher.on();
		
		//UPnPCipher.onCentralize();
		//UPnPCipher.setHost("192.168.0.101");
		//UPnPCipher.setPort(4558);
	}
	
	public boolean queryControlReceived(StateVariable stateVar)
	{
		stateVar.setValue(getCurrentTime());
		return true;
	}
	
	public boolean actionControlReceived(Action action){
		String actionName = action.getName();
		if (actionName.equals("GetTime") == true) {
			String dateStr = getCurrentTime();
			Argument timeArg = action.getArgument("CurrentTime");
			timeArg.setValue(dateStr);
			return true;
		}
		if (actionName.equals("TVPowerOn") == true) {
			action.getArgument("Result").setValue("1");

			//start avTransmitThread
			AVTransmitThread t = new AVTransmitThread();
			t.setDevice(this);
			t.start();
			
			return true;
		}	if (actionName.equals("TVPowerOff") == true) {
			action.getArgument("Result").setValue("0");
			//stopAVTransmit
			return true;
		}
		if (actionName.equals("SetTime") == true) {
			Argument timeArg = action.getArgument("NewTime");
			String newTime = timeArg.getValue();
			Argument resultArg = action.getArgument("Result");
			resultArg.setValue("Not implemented (" + newTime + ")");
			return true;
		}
		if(actionName.equals("GetKeyInformation")== true){
			Argument keyInfoArg = action.getArgument("KeyInformation");
			Argument shareKeyIDArg = action.getArgument("ShareKeyID");
			shareKeyIDArg.setValue(shareKeyID);
			Argument remoteKeyInfoArg = action.getArgument("RemoteKeyInformation");
			this.remotePubKey = remoteKeyInfoArg.getValue();
			
			String pubIDStr = readPublicStr();
			keyInfoArg.setValue(pubIDStr);
			return true;
		}
		if(actionName.equals("GetLifetimeSequenceBase") == true){
			Argument ltsbArg = action.getArgument("SequenceBase");
			String ltsb = getLifeTimeSequenceBased();
			tmpLTSB = ltsb;
			ltsbArg.setValue(ltsb);
			return true;
		}
		if(actionName.equals("Authenticate") == true){
			
			Argument sequenceBaseArg = action.getArgument("SequenceBase");
			String sequenceBase = sequenceBaseArg.getValue();
			Argument authSecretArg = action.getArgument("AuthSecret");
			String authSecret = authSecretArg.getValue();
			
			//get value			
			//final SystemInformation.CPUUsageSnapshot m_prevSnapshot =
			//	SystemInformation.makeCPUUsageSnapshot ();
			
			//authentication
			boolean result = authen(sequenceBase, authSecret);
			
			//cpu usage measure
			//final SystemInformation.CPUUsageSnapshot event = 
			//	SystemInformation.makeCPUUsageSnapshot ();
			
			
			//double receivedCPUUsage = 100.0 * SystemInformation.getProcessCPUUsage (m_prevSnapshot, event);
			//System.out.println("Authnode "+authNode+" "+receivedCPUUsage+" %");
			String resultStr = (result)?"1":"0";
			
			Argument resultArgument = action.getArgument("Result");
			resultArgument.setValue(resultStr);
			
			if(resultStr.equals("0")){ //authen failed
				return true;
			}
			
			Argument uuidArg = action.getArgument("SID");
			String uuid = UPnP.createUUID();
			uuidArg.setValue(uuid);
			
			//1. set uuid
			SecureNode secureNode = new SecureNode(uuid);
			//2. set pairwise secret
			secureNode.setSecret(shareSecret);
			secureNode.setRegistered(true);
			this.secureNodeList.add(secureNode);
			int authNode = this.secureNodeList.size();
			
			System.out.println(" joined_uuid="+uuid+"_count="+authNode);
			
			Argument groupkeyEncoded = action.getArgument("GroupKeyEncoded");
			Argument groupkeyID = action.getArgument("GroupKeyID");
			
			groupkeyEncoded.setValue(this.groupKeyEncoded);
			groupkeyID.setValue(this.groupKeyIDVar.getValue());
			
			
			return true;
		}
		
		if(actionName.equals("AuthenticatePKI") == true){
			//cpu load measure
			//final SystemInformation.CPUUsageSnapshot m_prevSnapshot =
			//	SystemInformation.makeCPUUsageSnapshot ();
			
			//get value
			String sequenceBase = action.getArgumentValue("SequenceBase");
			String authSecret = action.getArgumentValue("AuthSecret");
			
			//authentication
			boolean result = authenPKI(sequenceBase, authSecret);
			String resultStr = (result)?"1":"0";
			
			Argument resultArgument = action.getArgument("Result");
			resultArgument.setValue(resultStr);
			
			if(resultStr.equals("0")){
				return true;
			}
			
			// store secure node session
			
			Argument uuidArg = action.getArgument("SID");
			String uuid = UPnP.createUUID();
			uuidArg.setValue(uuid);
			
			//1. set uuid
			SecureNode secureNode = new SecureNode(uuid);
			//2. set publickey
			secureNode.setSecret(this.tmpCertInBytes);
			secureNode.setSessionKey(this.tmpSessionKey);
			
			secureNode.setRegistered(true);
			this.secureNodeList.add(secureNode);
			int authNode = this.secureNodeList.size();
			
			System.out.println(" joined_uuid_"+uuid+"_count_"+authNode);
			
			Argument groupkeyEncoded = action.getArgument("GroupKeyEncoded");
			Argument groupkeyID = action.getArgument("GroupKeyID");
			Argument sessionKey = action.getArgument("SessionKeyEncoded");
			
			//cpu usage measure
			groupkeyEncoded.setValue(this.groupKeyEncoded);
			groupkeyID.setValue(this.groupKeyIDVar.getValue());
			sessionKey.setValue(this.tmpSessionKeyEncoded);
			
			//final SystemInformation.CPUUsageSnapshot event = 
			//	SystemInformation.makeCPUUsageSnapshot ();
			
			
			//double receivedCPUUsage = 100.0 * SystemInformation.getProcessCPUUsage (m_prevSnapshot, event);
			//System.out.println("Auth node "+authNode+" "+receivedCPUUsage+" %");
			
			return true;
		}
		if(actionName.equals("GetGroupKey")== true){
			if(!isExecutable()){  //require decryptandexecuting and nodeRegistered
				action.setStatus(UPnPStatus.ACTION_NOT_AUTHORIZED);
				return false;
			}
			BASE64Encoder base64Enc = new BASE64Encoder();
			String groupKeyStr = base64Enc.encode(this.groupKey);
			Argument groupKeyArg = action.getArgument("GroupKey");
			//String pubIDStr = readPublicStr();
			
			groupKeyArg.setValue(groupKeyStr);
			return true;
		}
		if(actionName.equals("LeaveGroup")== true){
			if(!isDecryptAndExecuting()){  //require decryptandexecute only
				action.setStatus(UPnPStatus.ACTION_NOT_AUTHORIZED);
				return false;
			}
			
			//1. get sid
			String sid = action.getArgumentValue("SID");
			SecureNode node = this.secureNodeList.getSecureNode(sid);
			if(node == null){
				return false;
			}
		
			//2. unregister
			node.setRegistered(false);
			
			//3. rekey
			leaveRekey();
			action.getArgument("Result").setValue("1");

			return true;
		}
		if(actionName.equals("DecryptAndExecute") == true){
			//lock
			startDecryptAndExecuting();
			setNodeRegistration(false);
			
			Argument requestArgument = action.getArgument("Request");
			Argument resultArgument = action.getArgument("Result");
			
			
			String request = requestArgument.getValue();
			String requestSID = action.getArgumentValue("SID");
			byte[] secret =null;
			
			//System.out.println("DAE: SID = "+requestSID);
			
			
			if(requestSID.equals("") == true)
			{
				//if no sid specific using a current group key
				secret = this.groupKey;
				System.out.println("DAE: using group key");
			}else{
				SecureNode node = this.secureNodeList.getSecureNode(requestSID);
				//System.out.println("DAE: using session key");
				//System.out.println("DAE: node = "+node.getName()+" key length = "+node.getSessionKey().length);
				if(node == null){
					action.setStatus(UPnPStatus.ACTION_NOT_AUTHORIZED);
					return false;
				}
				//secret = node.getSecret(); //KPS
				secret = node.getSessionKey(); //PKI
				
				if(secret == null){
					action.setStatus(UPnPStatus.ACTION_NOT_AUTHORIZED);
					return false;
				}
				
				setNodeRegistration(node.isRegistered());
			}
			
			
			//step 1 decrypt
			String actionControlContent = decryptSOAP(secret, request);
			
			
			HTTPRequest httpRequest = new HTTPRequest(new ByteArrayInputStream(actionControlContent.getBytes()));
			//httpRequestRecieved(httpRequest);
			
			//step 2 execute
			String encResponse = executeSOAP(secret, httpRequest);
			
			
			resultArgument.setValue(encResponse);
			
			//unlock
			stopDecryptAndExecuting();
			setNodeRegistration(false);
			return true;
		}
		
		//PKI support
		if(actionName.equals("GetCertificate")==true ){
			
			//member's certificate
			String certificateIn = action.getArgumentValue("CertificateIn");
			try{
				//decode base64
				BASE64Decoder base64Dec = new BASE64Decoder();
				byte[] certBytes = base64Dec.decodeBuffer(certificateIn);
				/*
				ByteArrayInputStream bin = new ByteArrayInputStream(certBytes);
				
				//generate certificate
				CertificateFactory cf = CertificateFactory.getInstance("X509");
				Certificate certIn = cf.generateCertificate(bin);
				
				//verify certin
				loadCACertificate(caCertificatePath);
				certIn.verify(caCert.getPublicKey());
				*/
				//save certIn
				//this.tempCertIn = certIn;
				this.tmpCertInBytes = certBytes;
				
				
				//readlocal certificate
				byte[] localCertBytes = loadCertificate(certificatePath);
				BASE64Encoder base64Enc = new BASE64Encoder();
				String localCert = base64Enc.encode(localCertBytes);
				action.setArgumentValue("CertificateOut", localCert);
				
			}catch(Exception e){
				e.printStackTrace();
				return false;
			}
			
			return true;
		}
		
		if(actionName.equals("GetBroadcastVideoURI") == true){
			action.getArgument("BroadcastVideoURI").setValue(broadcastVideoURI);
			return true;
		}
		if(actionName.equals("GetBroadcastAudioURI") == true){
			action.getArgument("BroadcastAudioURI").setValue(broadcastAudioURI);
			return true;
		}
		if(actionName.equals("GetGroupID") == true){
			action.getArgument("GroupID").setValue(getGroupID());
			return true;
		}
		if(actionName.equals("GetGroupSize") == true){
			action.getArgument("GroupSize").setValue(getGroupSize());
			return true;
		}
		if(actionName.equals("GetCPUSpeed") == true){
			action.getArgument("CPUSpeed").setValue(getCPUSpeed());
			return true;
		}
		if(actionName.equals("GetFreeMemory") == true){
			action.getArgument("FreeMemory").setValue(getFreemem());
			return true;
		}
		if(actionName.equals("GetGroupControllerScore") == true){
			action.getArgument("GroupID").setValue(getGroupID());
			action.getArgument("GroupSize").setValue(getGroupSize());
			action.getArgument("CPUSpeed").setValue(getCPUSpeed());
			action.getArgument("FreeMemory").setValue(getFreemem());
			return true;
		}
		
		
		//unlock
		stopDecryptAndExecuting();
		return false;
	}
	

	/*
	 * #### MAIN FOR TESTING ONLY
	 */
	public static void main(String [] args)throws Exception{
		String file1 = "description/description_adevice_2.xml";
		//String file2 = "description/description_adevice_2.xml";
		DDevice d1 = new DDevice(file1);
		//DDevice d2 = new DDevice(file2);
		//d1.start();
		
		d1.start();
		//d2.setCPUSpeedVal("2600");
		//d2.start();
	
		Thread.currentThread().join();
	}
	
	private String getCurrentTime(){
		Calendar cal = Calendar.getInstance();
		String currentTime = cal.get(Calendar.HOUR)+":";
		currentTime += cal.get(Calendar.MINUTE);
		
		return currentTime;
	}
	
	private SecureNodeList secureNodeList = new SecureNodeList();
	
	
	public byte[] getGroupKey(){
		return this.groupKey;
	}
	
	public String getAVFilePath(){
		return this.avFilePath;
	}
	
	public String getBroadcastAddress(){
		return this.broadcastAddress;
	}
	
	public String getBroadcastPort(){
		return this.broadcastPort;
	}
	/*
	public void startAVTransmit(byte[] groupKey,String filePath, String address, String port){
		//1
		
		Format fmt = null;
		
		
		AVTransmit3 at = new AVTransmit3(new MediaLocator(filePath),
				          address, port, fmt, groupKey);
		
		String result = at.start();
		
		if (result != null) {
		    System.err.println("Error : " + result);
		    return;
		}
		
		System.err.println("Start transmission for 60 seconds...");
		
		try {
		    Thread.currentThread().sleep(60000);
		} catch (InterruptedException ie) {}
		
		at.stop();
	}*/
	
	private String cpuSpeedMHz = "2600";
	private String groupID = "000-001";
	
	public void setCPUSpeedVal(String cpuSpeedVal){
		this.cpuSpeedMHz = cpuSpeedVal;
	}
	
	//todo 1
	public String getGroupID(){
		return this.groupID;
	}
	
	//todo 2
	public String getGroupSize(){
		int groupSize = this.secureNodeList.getRegisteredNode();
		return ""+groupSize;
	}
	
	//todo 3
	public String getCPUSpeed(){
		return this.cpuSpeedMHz;
	}
	
	//todo 4
	//get free mem of jvm measure in byte
	public String getFreemem(){
		//long totalMem = Runtime.getRuntime().totalMemory();
		long freeMem = Runtime.getRuntime().freeMemory();
		return ""+freeMem;
	}
	
	//PKI support
	private String caCertificatePath = "CA\\m0_cert\\cacert.der";
	private String certificatePath = "CA\\m0_cert\\newcert.der";
	private String privateKeyPath = "CA\\m0_cert\\newkey.der";
	
	//PKI support
	private Certificate caCert = null;
	private Certificate tempCertIn = null;
	private byte[] tmpCertInBytes = null;
	//private RSAPrivateKey privKey = null;

	//load own certificate (will sent to network)
	private byte[] loadCertificate(String certPath){
		File certFile = new File(certPath);
		
		try{//read cacert file
			DataInputStream dis = new DataInputStream(new FileInputStream(certFile));
			CertificateFactory cf = CertificateFactory.getInstance("X509");
			Certificate cert = cf.generateCertificate(dis);
			
			
			return cert.getEncoded();
			
		}catch(Exception e){
			e.printStackTrace();
		}
		return null;
	}
	
	private byte[] loadPrivKey(String privKeyPath){
		//DER format
		File privKeyFile = new File(privKeyPath);
		
		try{
			//read private key DER file
			DataInputStream dis = new DataInputStream(new FileInputStream(privKeyFile));
			byte[] privKeyBytes = new byte[(int)privKeyFile.length()];
			dis.read(privKeyBytes);
			dis.close();
			
			return privKeyBytes;
			//KeyFactory keyFactory = KeyFactory.getInstance("RSA");
			
			//decode private key
			//PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(privKeyBytes);
			//RSAPrivateKey privKey = (RSAPrivateKey)keyFactory.generatePrivate(privSpec);
			
			//this.privKey = privKey;
			
			
		}catch(Exception e){
			e.printStackTrace();
		}
		
		return null;
	}
	
	private boolean loadCACertificate(String caCertPath){
		//der format
		if(this.caCert != null)
			return false;
		
		File cacertFile = new File(caCertPath);
		
		try{
			//read cacert file
			DataInputStream dis = new DataInputStream(new FileInputStream(cacertFile));
			CertificateFactory cf = CertificateFactory.getInstance("X509");
			Certificate cert = cf.generateCertificate(dis);
			this.caCert = cert;
			
			return true;
			
		}catch(Exception e){
			e.printStackTrace();
		}
		return false;
	}

}
